package com.blocks;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.blocks.blocks.Block;
import com.blocks.blocks.ColorBlock;
import com.blocks.blocks.DetachedBlock;
import com.blocks.blocks.MasterBlock;
import com.blocks.blocks.ScriptEvaluator;
import com.blocks.blocks.SmokeBlock;
import com.blocks.blocks.SoundBlock;
import com.blocks.controller.BlockActionListener;
import com.blocks.controller.BlockEvent;
import com.blocks.model.LightningNode;
import com.blocks.network.BlockClient;
import com.blocks.network.BlockServer;
import com.blocks.network.BlockServer.ChatMessage;
import com.blocks.script.ScriptUtils;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.TextureKey;
import com.jme3.audio.AudioNode;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.network.Client;
import com.jme3.niftygui.NiftyJmeDisplay;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.shadow.BasicShadowRenderer;
import com.jme3.system.AppSettings;
import com.jme3.texture.Texture;

import de.lessvoid.nifty.Nifty;

public class ScriptBlocksApplication extends SimpleApplication {

	public static final String SCRIPT_BLOCK_FOLDER = "/blocks";
	public static final String CONFIG_FILE_FOLDER = "/config";
	public static final String CONFIG_FILE = "config.js";
	public static final String CONFIG_FILE_INIT = "/config/config.js";
	
	
	
	public static final float FOCUS_DISTANCE = 40.0f;
	private static final float BLOCK_MOVE = 100;
	public static final float FOCUS_TIME = 0.0f;
	public static final float BLOCK_UPDATE_TIME = 0.0f;
	public static final float UNPOWERED_BLOCK_UPDATE_TIME = 0.0f;
	
	private float moveAmount =0;//used with LOCK_TO_GRID
	private String scriptDir;
	private String copyBuffer;
	private String copyBlockVariable;
	private String undoBuffer;
	private String undoBlockVariable;
	private Block undoBlock;// Holds the Parent of the block that is being cut
	private Map<Integer, Integer> wirelessFrequencyMap = new HashMap<Integer, Integer>();

	private List<BlockEvent> blockEventList = new LinkedList<BlockEvent>();

	private GameState gameState = GameState.GUI;
	private Map<Long, MasterBlock> masterBlockMap = new HashMap<Long, MasterBlock>();
	private Map<Long, MasterBlock> tempMap = new HashMap<Long, MasterBlock>();
	private Map<Long, Block> blockTypeMap = new TreeMap<Long, Block>();
	private BulletAppState bulletAppState;
	Material mat;
	Long hudBlockIndex;
	Vector3f originMovement = Vector3f.ZERO;
	private Block masterMoveBlock; // Block that is used with the numpad keys to
									// move Master Block around(Last Master
									// Block to have Focus)

	Block focusBlock;;
	int focusBlockTriangle;

	float focusLastTime = 0;
	Vector3f lastCamDir = Vector3f.ZERO;
	Vector3f lastCamLocation = Vector3f.ZERO;
	Node blockNode;
	BlockHandler blockHandler = new BlockHandler();
	int colorIndex = 0;

	private boolean fly = false;

	private CharacterControl player;
	private Vector3f walkDirection = new Vector3f();
	private boolean left = false, right = false, up = false, down = false;

	private boolean blockLeft = false, blockRight = false,
			blockForward = false, blockBackward = false, blockUp = false,
			blockDown = false;

	/** Activate custom rendering of shadows */
	BasicShadowRenderer bsr;

	private AudioNode audio_gun;

	Geometry leftMark;
	Geometry rightMark;
	Vector2f SCALE_TEXTURE_VALUE = new Vector2f(4.0f, 4.0f);

	public BitmapText cameraDirectionText;
	private BitmapText blockSelectText;
	private BitmapText infoSelectText;
	private Nifty nifty;
	private Client client;

	private String terminalValue = "";
	private boolean MULTIPLAYER = false;

	private boolean niftyActive = false;
	private ScriptEvaluator scriptEvaluator;

	private LevelProperties levelProperties = new LevelProperties();

	private static ScriptBlocksApplication instance;

	public static ScriptBlocksApplication getInstance() {
		if (instance == null) {
			instance = new ScriptBlocksApplication();
		}
		return instance;
	}

	private BlockActionListener actionListener = new BlockActionListener();

	private Node cameraNode = new CameraNode();
	Block pickBlock;
	private String scriptName;
	private Material floorMaterial;
	private Spatial skyMap;

	@Override
	public void start() {

		boolean loadSettings = false;
		if (settings == null) {
			setSettings(new AppSettings(true));
			loadSettings = true;
		}
		settings.setSettingsDialogImage("/Textures/scriptblocks_logo.png");
		settings.setTitle("Script Blocks");
		super.start();
	}

	@Override
	public void simpleInitApp() {

		inputManager.setCursorVisible(true);
		for (int x = 0; x < 100; x++) {
			tempMap.put(new Long(x), new MasterBlock("m" + x, new Long(x)));
		}

		bulletAppState = new BulletAppState();
		stateManager.attach(bulletAppState);
		// bulletAppState.getPhysicsSpace().enableDebug(assetManager);
		// bulletAppState.getPhysicsSpace().disableDebug();
		flyCam.setMoveSpeed(100);
		if(!levelProperties.isShowStats()){
			removeStats();
		}	
		cam.setLocation(new Vector3f(0f, 0f, 0f));
		cam.lookAt(new Vector3f(0f, 0f, 10f), Vector3f.UNIT_Y);

		initControls();
		initCrossHairs();
		initBlockTypeMap();
		initAudio();
		initFloor();
		initHUD();
		initScriptBlockList();
		
		
		addShadow();
		addSkyMap();
		if (MULTIPLAYER) {
			initBlockClient();
		}
//		LightningNode lbn = new LightningNode("Lightning" , new Vector3f(20.0f, 0.0f, 0.0f));
//		lbn.setLocalTranslation(0.0f, 20.0f, 0.0f);		
//		rootNode.attachChild(lbn);
		initNiftyGui();
		
	}

	public void addSkyMap() {
		// west, east, north, south, up, down
		skyMap = com.jme3.util.SkyFactory.createSky(assetManager,

		assetManager
				.loadTexture(new TextureKey("Textures/Sky/west.jpg", true)),
				assetManager.loadTexture(new TextureKey(
						"Textures/Sky/east.jpg", true)), assetManager
						.loadTexture(new TextureKey("Textures/Sky/north.jpg",
								true)), assetManager
						.loadTexture(new TextureKey("Textures/Sky/south.jpg",
								true)),

				assetManager.loadTexture(new TextureKey("Textures/Sky/up.jpg",
						true)), assetManager.loadTexture(new TextureKey(
						"Textures/Sky/down.jpg", true)));
		rootNode.attachChild(skyMap);

	}
	public void removeSkyMap() {
		if(skyMap!=null){		
		rootNode.detachChild(skyMap);
		}
	}

	public void addShadow() {
		bsr = new BasicShadowRenderer(assetManager, 512);
		// bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
		bsr.setDirection(new Vector3f(0, -1, 0).normalizeLocal());
		viewPort.addProcessor(bsr);

	}
	public void removeStats() {		
			 guiNode.detachChild(statsView);
			 guiNode.detachChild(fpsText);		
	}
	public void addStats() {
		guiNode.attachChild(statsView);
		guiNode.attachChild(fpsText);
		
	}
	public void removeShadow() {
		viewPort.removeProcessor(bsr);
	}

	private void initControls() {

		inputManager.addMapping("Remove", new KeyTrigger(KeyInput.KEY_X));
		inputManager.addMapping("Undo", new KeyTrigger(KeyInput.KEY_U));
		inputManager.addListener(actionListener, "Remove", "Undo");
		inputManager.addMapping("shootRight", new MouseButtonTrigger(
				MouseInput.BUTTON_RIGHT));
		inputManager.addListener(actionListener, "shootRight");
		// inputManager.addMapping("joint", new KeyTrigger(KeyInput.KEY_J));
		// inputManager.addListener(actionListener, "joint");
		// inputManager.addMapping("texture", new KeyTrigger(KeyInput.KEY_T));
		// inputManager.addListener(actionListener, "texture");

		inputManager.deleteTrigger("SIMPLEAPP_CameraPos", new KeyTrigger(
				KeyInput.KEY_C));

		inputManager.addMapping("Copy", new KeyTrigger(KeyInput.KEY_C));
		inputManager.addListener(actionListener, "Copy");

		inputManager.addMapping("Paste", new KeyTrigger(KeyInput.KEY_V));
		inputManager.addListener(actionListener, "Paste");

		// player keys
		inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
		inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
		inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
		inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
		// inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE));
		inputManager.addMapping("Shoot", new KeyTrigger(KeyInput.KEY_R));

		inputManager.addListener(actionListener, "Lefts");
		inputManager.addListener(actionListener, "Rights");
		inputManager.addListener(actionListener, "Ups");
		inputManager.addListener(actionListener, "Downs");
		// inputManager.addListener(actionListener, "Jumps");
		inputManager.addListener(actionListener, "Shoot");

		// Block Choice

		inputManager.addMapping("Use", new KeyTrigger(KeyInput.KEY_E));
		inputManager.addListener(actionListener, "Use");

		// Move the Master Block
		inputManager.addMapping("BlockLeft", new KeyTrigger(
				KeyInput.KEY_NUMPAD4), new KeyTrigger(KeyInput.KEY_LEFT));
		inputManager.addListener(actionListener, "BlockLeft");
		inputManager.addMapping("BlockRight", new KeyTrigger(
				KeyInput.KEY_NUMPAD6), new KeyTrigger(KeyInput.KEY_RIGHT));
		inputManager.addListener(actionListener, "BlockRight");
		inputManager.addMapping("BlockForward", new KeyTrigger(
				KeyInput.KEY_NUMPAD8), new KeyTrigger(KeyInput.KEY_UP));
		inputManager.addListener(actionListener, "BlockForward");
		inputManager.addMapping("BlockBackward", new KeyTrigger(
				KeyInput.KEY_NUMPAD2), new KeyTrigger(KeyInput.KEY_DOWN));
		inputManager.addListener(actionListener, "BlockBackward");
		inputManager.addMapping("BlockUp", new KeyTrigger(KeyInput.KEY_PGUP));
		inputManager.addListener(actionListener, "BlockUp");
		inputManager.addMapping("BlockDown", new KeyTrigger(KeyInput.KEY_PGDN));
		inputManager.addListener(actionListener, "BlockDown");

		inputManager.addMapping("BlockPower", new KeyTrigger(
				KeyInput.KEY_NUMPAD5), new KeyTrigger(KeyInput.KEY_P));
		inputManager.addListener(actionListener, "BlockPower");

		inputManager.addMapping("Fly", new KeyTrigger(KeyInput.KEY_F));
		inputManager.addListener(actionListener, "Fly");

		inputManager.addMapping("MoveMouse", new MouseAxisTrigger(
				MouseInput.AXIS_X, false), new MouseAxisTrigger(
				MouseInput.AXIS_X, true), new MouseAxisTrigger(
				MouseInput.AXIS_Y, false), new MouseAxisTrigger(
				MouseInput.AXIS_Y, true));
		inputManager.addListener(actionListener, "MoveMouse");
		inputManager.addMapping("MoveMouseWheelNegative", new MouseAxisTrigger(
				MouseInput.AXIS_WHEEL, true));
		inputManager.addListener(actionListener, "MoveMouseWheelNegative");
		inputManager.addMapping("MoveMouseWheel", new MouseAxisTrigger(
				MouseInput.AXIS_WHEEL, false));
		inputManager.addListener(actionListener, "MoveMouseWheel");

		inputManager.deleteMapping("SIMPLEAPP_Exit");

		inputManager.deleteMapping("FLYCAM_ZoomIn");
		inputManager.deleteMapping("FLYCAM_ZoomOut");

		inputManager.deleteTrigger("FLYCAM_Left", new KeyTrigger(
				KeyInput.KEY_LEFT));
		inputManager.deleteTrigger("FLYCAM_Right", new KeyTrigger(
				KeyInput.KEY_RIGHT));
		inputManager
				.deleteTrigger("FLYCAM_Up", new KeyTrigger(KeyInput.KEY_UP));
		inputManager.deleteTrigger("FLYCAM_Down", new KeyTrigger(
				KeyInput.KEY_DOWN));

		inputManager.addMapping("Menu", new KeyTrigger(KeyInput.KEY_ESCAPE));
		inputManager.addListener(actionListener, "Menu");

		// inputManager.addMapping("Terminal", new
		// KeyTrigger(KeyInput.KEY_TAB));
		// inputManager.addListener(actionListener, "Terminal");

		inputManager
				.addMapping("Inventory", new KeyTrigger(KeyInput.KEY_SPACE));
		inputManager.addListener(actionListener, "Inventory");
		inputManager.addMapping("Help", new KeyTrigger(KeyInput.KEY_H));
		inputManager.addListener(actionListener, "Help");

		// Inventory
		inputManager.addMapping("Inventory1", new KeyTrigger(KeyInput.KEY_1));
		inputManager.addMapping("Inventory2", new KeyTrigger(KeyInput.KEY_2));
		inputManager.addMapping("Inventory3", new KeyTrigger(KeyInput.KEY_3));
		inputManager.addMapping("Inventory4", new KeyTrigger(KeyInput.KEY_4));
		inputManager.addMapping("Inventory5", new KeyTrigger(KeyInput.KEY_5));
		inputManager.addMapping("Inventory6", new KeyTrigger(KeyInput.KEY_6));
		inputManager.addMapping("Inventory7", new KeyTrigger(KeyInput.KEY_7));
		inputManager.addMapping("Inventory8", new KeyTrigger(KeyInput.KEY_8));
		inputManager.addMapping("Inventory9", new KeyTrigger(KeyInput.KEY_9));
		inputManager.addMapping("Inventory0", new KeyTrigger(KeyInput.KEY_0));

		inputManager.addListener(actionListener, "Inventory1");
		inputManager.addListener(actionListener, "Inventory2");
		inputManager.addListener(actionListener, "Inventory3");
		inputManager.addListener(actionListener, "Inventory4");
		inputManager.addListener(actionListener, "Inventory5");
		inputManager.addListener(actionListener, "Inventory6");
		inputManager.addListener(actionListener, "Inventory7");
		inputManager.addListener(actionListener, "Inventory8");
		inputManager.addListener(actionListener, "Inventory9");
		inputManager.addListener(actionListener, "Inventory0");

	}

	private void initFloor() {
		// String textureImage = "Textures/TestDepthCircle.png";
		String textureImage = "Textures/Grid.tga";
		Box boxshape1 = new Box(new Vector3f(0.0f, 0.0f, 0f), 500f, 1f, 500f);
		Geometry floor = new Geometry("Floor", boxshape1);
		floorMaterial = new Material(assetManager,
				"Common/MatDefs/Misc/Unshaded.j3md");
		Texture tex_ml = assetManager.loadTexture(textureImage);
		floorMaterial.setTexture("ColorMap", tex_ml);
		floor.setMaterial(floorMaterial);
		rootNode.attachChild(floor);
		
		floor.setShadowMode(ShadowMode.Receive);
		

	}

	public void initNiftyGui() {
		guiState();
		String nextLevel = ScriptBlocksApplication.getInstance().getLevelProperties().getNextLevelFileName();
		if(nextLevel==null || nextLevel.trim().length()==0){
			ScriptBlocksApplication.getInstance().initScriptSave("level1.js");
		}
		Logger.getLogger("de.lessvoid").setLevel(Level.SEVERE);
		if (isNiftyActive()) {
			return;
		} else {
			setNiftyActive(true);
		}
		NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
				inputManager, audioRenderer, guiViewPort);
		nifty = niftyDisplay.getNifty();
		guiViewPort.addProcessor(niftyDisplay);
		
		nifty.fromXml("Interfaces/mainmenu.xml", "start");

		

	}

	public void initNiftyTerminal() {
		guiState();
		Logger.getLogger("de.lessvoid").setLevel(Level.OFF);
		if (isNiftyActive()) {
			return;
		} else {
			setNiftyActive(true);
		}
		NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
				inputManager, audioRenderer, guiViewPort);
		nifty = niftyDisplay.getNifty();
		guiViewPort.addProcessor(niftyDisplay);

		nifty.fromXml("Interfaces/terminal.xml", "terminal");

		// attach the nifty display to the gui view port as a processor

		

	}

	public void initNiftyInventory() {
		guiState();
		Logger.getLogger("de.lessvoid").setLevel(Level.SEVERE);
		if (isNiftyActive()) {
			return;
		} else {
			setNiftyActive(true);
		}
		NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
				inputManager, audioRenderer, guiViewPort);
		nifty = niftyDisplay.getNifty();
		guiViewPort.addProcessor(niftyDisplay);

		nifty.fromXml("Interfaces/inventory.xml", "inventory");

		

	}

	public void initNiftyHelp() {
		guiState();
		Logger.getLogger("de.lessvoid").setLevel(Level.SEVERE);
		if (isNiftyActive()) {
			return;
		} else {
			setNiftyActive(true);
		}
		NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
				inputManager, audioRenderer, guiViewPort);
		nifty = niftyDisplay.getNifty();
		guiViewPort.addProcessor(niftyDisplay);

		nifty.fromXml("Interfaces/help.xml", "help");

		

	}
	

	public void initBlockClient() {
		BlockServer.initializeClasses();
		BlockClient blockClient;
		try {
			blockClient = new BlockClient("localhost");

			client = blockClient.getClient();
			ChatMessage chat = new ChatMessage("HELLO", "PING");
			chat.setReliable(true);
			client.send(chat);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void guiState() {
		inputManager.setCursorVisible(true);
		flyCam.setEnabled(false);
		setGameState(GameState.GUI);
	}

	public void gameState() {
		// System.out.println(this + "gameState()");
		inputManager.setCursorVisible(false);
		flyCam.setEnabled(true);
		setGameState(GameState.GAME);
	}

	public void initScriptBlockList() {
		File directory;
		directory = new File(this.getScriptDir()
				+ ScriptBlocksApplication.SCRIPT_BLOCK_FOLDER);
		if (directory != null) {
			System.out.println("GET SCRIPTS FROM PATH"
					+ directory.getAbsolutePath());
			Config.getInstance().addAllFilesInDirectory(directory);
		}

	}

	// Will load the saved game
	// Will setup the script evaluator for the game simpleUpdate

	public void initScriptSave(String fileName) {
		// Config.getInstance().loadConfig(this.getScriptDir()+ScriptBlocksApplication.CONFIG_FILE_INIT);
		if (fileName != null) {
			this.setScriptName(ScriptBlocksApplication.CONFIG_FILE_FOLDER + "/"
					+ fileName);
		} else {
			this.setScriptName(ScriptBlocksApplication.CONFIG_FILE_INIT);
		}

		if (getScriptName() != null) {
			ScriptEvaluator scriptEvaluator = new ScriptEvaluator();
			this.setScriptEvaluator(scriptEvaluator);
			try {
				scriptEvaluator.evalutate(ScriptBlocksApplication.getInstance()
						.getScriptDir() + getScriptName());
			} catch (Exception ev) {
				ev.printStackTrace();
			}
		}
		if (this.scriptEvaluator != null) {
			scriptEvaluator.setupScript();
		}
	}

	@Override
	public void simpleUpdate(float tpf) {
		if(GameState.GUI.equals(getGameState())){
			return;
		}
		if (cam.getLocation().getY() < 2.0) {
			cam.setLocation(cam.getLocation().add(
					new Vector3f(0.0f, 1.0f, 0.0f)));
		}
		Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
		Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
		Vector3f camLocation = cam.getLocation().clone();
		boolean cameraMoved = false;
		if (!lastCamDir.equals(camDir) || !lastCamLocation.equals(camLocation)) {
			cameraMoved = true;
			lastCamDir = camDir.clone();
			lastCamLocation = camLocation.clone();
		}
		walkDirection.set(0, 0, 0);
		if (left) {
			walkDirection.addLocal(camLeft);
		}
		if (right) {
			walkDirection.addLocal(camLeft.negate());
		}
		if (up) {
			walkDirection.addLocal(camDir);
		}
		if (down) {
			walkDirection.addLocal(camDir.negate());
		}
		boolean moveMasterBlock = false;
		if(levelProperties.isLockMasterToGrid()){
			moveAmount = moveAmount+tpf;
			if(moveAmount>0.1){
				moveMasterBlock = true;
				moveAmount = 2;
			}
		}else {
			moveMasterBlock = true;
			moveAmount = tpf * BLOCK_MOVE;
		}
		if(moveMasterBlock){
			if (blockLeft) {
				moveMaster(new Vector3f(moveAmount, 0.0f, 0.0f));
			}
			if (blockRight) {
				moveMaster(new Vector3f(-moveAmount, 0.0f, 0.0f));
			}
			if (blockUp) {
				moveMaster(new Vector3f(0.0f, moveAmount, 0.0f));
			}
			if (blockDown) {
				moveMaster(new Vector3f(0.0f, -moveAmount, 0.0f));
			}
			if (blockForward) {
				moveMaster(new Vector3f(0.0f, 0.0f, moveAmount));
			}
			if (blockBackward) {
				moveMaster(new Vector3f(0.0f, 0.0f, -moveAmount));
	
			}
			if(levelProperties.isLockMasterToGrid()){				
				if(moveAmount==2){
					moveAmount = 0;
				}
			}
		}
		// player.setWalkDirection(walkDirection);
		// cam.setLocation(player.getPhysicsLocation());
		cameraNode.setLocalRotation(cam.getRotation());
		cameraNode.setLocalTranslation(cam.getLocation());
		this.getPickBlock().setLocalRotation(cam.getRotation());

		// Update HUD
		cameraDirectionText
				.setText("Select Menu(Esc) Select Block(Space) Help(H)");
		blockSelectText.setText(this.getBlockTypeMap()
				.get(this.getHudBlockIndex()).getName());
		// blockHandler.traversePowerBlockinClusters();

		// Audio Ear
		getListener().setLocation(cam.getLocation());
		getListener().setRotation(cam.getRotation());
       
		if (cameraMoved && focusLastTime >= FOCUS_TIME) {
			this.actionListener.focus();
			focusLastTime = 0;
		} else {
			focusLastTime = focusLastTime + tpf;
		}
		if (this.scriptEvaluator != null) {
			scriptEvaluator.loopScript(tpf);
		}
		if (getBlockEventList().contains(BlockEvent.LEVEL_COMPLETED)) {
			endLevel(BlockEvent.LEVEL_COMPLETED);
		} else if (getBlockEventList().contains(BlockEvent.LEVEL_FAILED)) {
			endLevel(BlockEvent.LEVEL_FAILED);
		}
//		String wirelessValues = "";
//
//		for (Integer key : this.getWirelessFrequencyMap().keySet()) {
//			wirelessValues = wirelessValues + "[" + key + ","
//					+ this.getWirelessFrequencyMap().get(key) + "]";
//		}
//		infoSelectText.setText(wirelessValues);

	}

	private void moveMaster(Vector3f vector3f) {
		Block block = getMasterMoveBlock();
		if (block != null && block instanceof MasterBlock) {
			MasterBlock masterBlock = (MasterBlock) block;
			masterBlock.setLocalTranslation(masterBlock.getLocalTranslation()
					.add(vector3f));
		}
	}

	public void endLevel(BlockEvent blockEvent) {
		if (BlockEvent.LEVEL_FAILED.equals(blockEvent)) {
			floorMaterial.setColor("Color", ColorRGBA.Red);
			ScriptBlocksApplication.getInstance().cameraDirectionText
					.setText("Level Failed");
			if(ScriptBlocksApplication.getInstance().getLevelProperties().getLevelFileName()!=null){
				newGame();
				loadLevel(ScriptBlocksApplication.getInstance().getLevelProperties().getLevelFileName(),true);
			}
			
		} else {
			floorMaterial.setColor("Color", ColorRGBA.Green);
			ScriptBlocksApplication.getInstance().cameraDirectionText
					.setText("Level Completed");
			if(ScriptBlocksApplication.getInstance().getLevelProperties().getNextLevelFileName()!=null){
				newGame();
				System.out.println("LOADING LEVEL" + ScriptBlocksApplication.getInstance().getLevelProperties().getNextLevelFileName());
				loadLevel(ScriptBlocksApplication.getInstance().getLevelProperties().getNextLevelFileName(),true);
			}
		}

	}

	public void resetLevel() {
		clearMasterBlockMap();		
		setBlockEventList(new LinkedList<BlockEvent>());
		floorMaterial.setColor("Color", ColorRGBA.White);
		wirelessFrequencyMap = new HashMap<Integer, Integer>();		

	}

	public void playerWalk() {
		this.setFly(false);
	}

	public void playerFly() {
		this.setFly(true);
	}

	/** We create two audio nodes. */
	private void initAudio() {
		/* gun shot sound is to be triggered by a mouse click. */
		audio_gun = new AudioNode(audioRenderer, assetManager,
				"Sounds/bird_splat.wav", false);
		audio_gun.setLooping(false);
		// Volume goes from 0.1f to 1.0f. At 0 it's muted.
		audio_gun.setVolume(1.0f);

	}

	protected void initCrossHairs() {
		// guiNode.detachAllChildren();
		guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
		BitmapText ch = new BitmapText(guiFont, false);
		ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
		ch.setText("+"); // crosshairs
		ch.setLocalTranslation(
				// center
				settings.getWidth() / 2
						- guiFont.getCharSet().getRenderedSize() / 3 * 2,
				settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
		guiNode.attachChild(ch);
	}

	private void initHUD() {
		guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
		cameraDirectionText = new BitmapText(guiFont, false);
		cameraDirectionText.setSize(guiFont.getCharSet().getRenderedSize());
		cameraDirectionText.setText("Welcome");
		cameraDirectionText.setLocalTranslation(10, settings.getHeight() - 20,
				0);
		guiNode.attachChild(cameraDirectionText);

		blockSelectText = new BitmapText(guiFont, false);
		blockSelectText.setSize(guiFont.getCharSet().getRenderedSize());
		blockSelectText.setText("Block");
		blockSelectText.setLocalTranslation((settings.getWidth() - 60) / 2, 50,
				0);
		guiNode.attachChild(blockSelectText);

		infoSelectText = new BitmapText(guiFont, false);
		infoSelectText.setSize(guiFont.getCharSet().getRenderedSize());
		infoSelectText.setText("Please donate scriptblocks.com");
		infoSelectText.setLocalTranslation((settings.getWidth() / 2 + 60),
				settings.getHeight() - 20, 0);
		guiNode.attachChild(infoSelectText);

		Block pickBlock = new Block("ONE", null);
		pickBlock.init();
		setupPickBlock(pickBlock);
		cameraNode.attachChild(this.getPickBlock());
		rootNode.attachChild(cameraNode);
	}

	public void initBlockTypeMap() {
		this.getBlockTypeMap().put(new Long(0),
				new ColorBlock("COLOR", new Long(-1)));
		this.getBlockTypeMap().put(new Long(1),
				new SmokeBlock("SMOKE", new Long(-1)));
		this.getBlockTypeMap().put(new Long(2),
				new SoundBlock("SOUND", new Long(-1)));
		this.getBlockTypeMap().put(new Long(3),
				new DetachedBlock("DETACHED", new Long(-1)));
		this.setHudBlockIndex(new Long(0));
	}

	public Long getHudBlockIndex() {
		return hudBlockIndex;
	}

	public void setHudBlockIndex(Long hudBlockIndex) {
		this.hudBlockIndex = hudBlockIndex;
	}

	public void setHudBlockIndex(String blockName) {
		Map<Long, Block> blockTypeMap = ScriptBlocksApplication.getInstance()
				.getBlockTypeMap();
		for (Long id : blockTypeMap.keySet()) {
			if (blockName.equals(blockTypeMap.get(id).getName())) {
				this.hudBlockIndex = id;
				return;
			}
		}
		this.hudBlockIndex = 0L;
	}

	public void setupPickBlock(Block pickBlock) {
		if (ScriptBlocksApplication.getInstance().getPickBlock() != null) {
			cameraNode.detachChild(ScriptBlocksApplication.getInstance()
					.getPickBlock());
		}
		pickBlock.scale(0.1f);
		pickBlock.setLocalTranslation(new Vector3f(0.0f, -0.5f, 2.0f));
		pickBlock.setHudBlock(true);
		this.setPickBlock(pickBlock);
		cameraNode.attachChild(ScriptBlocksApplication.getInstance()
				.getPickBlock());
	}

	public Camera getCam() {
		return cam;
	}

	public CharacterControl getPlayer() {
		return player;
	}

	public void setPlayer(CharacterControl player) {
		this.player = player;
	}

	public Vector3f getWalkDirection() {
		return walkDirection;
	}

	public void setWalkDirection(Vector3f walkDirection) {
		this.walkDirection = walkDirection;
	}

	public boolean isLeft() {
		return left;
	}

	public void setLeft(boolean left) {
		this.left = left;
	}

	public boolean isRight() {
		return right;
	}

	public void setRight(boolean right) {
		this.right = right;
	}

	public boolean isUp() {
		return up;
	}

	public void setUp(boolean up) {
		this.up = up;
	}

	public boolean isDown() {
		return down;
	}

	public void setDown(boolean down) {
		this.down = down;
	}

	public boolean isBlockLeft() {
		return blockLeft;
	}

	public boolean isBlockRight() {
		return blockRight;
	}

	public boolean isBlockForward() {
		return blockForward;
	}

	public boolean isBlockBackward() {
		return blockBackward;
	}

	public boolean isBlockUp() {
		return blockUp;
	}

	public boolean isBlockDown() {
		return blockDown;
	}

	public void setBlockLeft(boolean blockLeft) {
		this.blockLeft = blockLeft;
	}

	public void setBlockRight(boolean blockRight) {
		this.blockRight = blockRight;
	}

	public void setBlockForward(boolean blockForward) {
		this.blockForward = blockForward;
	}

	public void setBlockBackward(boolean blockBackward) {
		this.blockBackward = blockBackward;
	}

	public void setBlockUp(boolean blockUp) {
		this.blockUp = blockUp;
	}

	public void setBlockDown(boolean blockDown) {
		this.blockDown = blockDown;
	}

	public AudioNode getAudioGun() {
		return audio_gun;
	}

	public void setAudioGun(AudioNode audioGun) {
		this.audio_gun = audioGun;
	}

	public BlockHandler getBlockHandler() {
		return this.blockHandler;
	}

	public Block getFocusBlock() {
		return focusBlock;
	}

	public void setFocusBlock(Block focusBlock) {
		this.focusBlock = focusBlock;
	}

	public Block getMasterMoveBlock() {
		return masterMoveBlock;
	}

	public void setMasterMoveBlock(Block masterMoveBlock) {
		this.masterMoveBlock = masterMoveBlock;
	}

	public int getFocusBlockTriangle() {
		return focusBlockTriangle;
	}

	public void setFocusBlockTriangle(int focusBlockTriangle) {
		this.focusBlockTriangle = focusBlockTriangle;
	}

	public Vector3f getOriginMovement() {
		return originMovement;
	}

	public void setOriginMovement(Vector3f originMovement) {
		this.originMovement = originMovement;
	}

	public boolean isFly() {
		return fly;
	}

	public void setFly(boolean fly) {
		this.fly = fly;
	}

	public Block getPickBlock() {
		return pickBlock;
	}

	public void setPickBlock(Block pickBlock) {
		this.pickBlock = pickBlock;
	}

	public MasterBlock getMasterBlock(Long masterBockId) {
		return masterBlockMap.get(masterBockId);
	}

	public void addMasterBlock(Long masterBlockId, MasterBlock masterBlock) {
		if (masterBlockMap.containsKey(masterBlockId)) {
//			System.out
//					.println("ScriptBlockApplication.addMasterBlock() : MASTER ID ALREADY EXISTS "
//							+ masterBlockId + " Old Block Destroyed");
			MasterBlock oldMasterBlock = this.masterBlockMap.get(masterBlockId);
			oldMasterBlock.destroy();
		}
		this.masterBlockMap.put(masterBlockId, masterBlock);
	}

	public void removeMasterBlock(Long masterBlockId) {
		this.masterBlockMap.remove(masterBlockId);
		Iterator it = masterBlockMap.keySet().iterator();
		while (it.hasNext()) {
			Long masterId = (Long) it.next();
		}
	}

	public Map<Long, MasterBlock> getMasterBlockMap() {
		return masterBlockMap;
	}

	public void clearMasterBlockMap() {
		masterBlockMap = new HashMap<Long, MasterBlock>();
	}

	public Map<Long, Block> getBlockTypeMap() {
		return blockTypeMap;
	}

	public void setBlockTypeMap(Map<Long, Block> blockTypeMap) {
		this.blockTypeMap = blockTypeMap;
	}

	public Block getNewBlock(String name) {
		try {
			Block hudBlock = ScriptBlocksApplication
					.getInstance()
					.getBlockTypeMap()
					.get(ScriptBlocksApplication.getInstance()
							.getHudBlockIndex());
			Block block;
			block = hudBlock.getClass().newInstance();
			block.setName(block.getClass().getSimpleName());
			block.setScriptName(hudBlock.getScriptName());
			block.setMasterId(focusBlock.getMasterId());
			return block;
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}

	public void sendMessage(String string) {
		if (MULTIPLAYER) {
			System.out.println("sendMessage" + string);
			ChatMessage chat = new ChatMessage(string,
					"ScriptBlockApplication.sendMessage()");
			chat.setReliable(true);
			client.send(chat);
		}

	}

	public String getTerminalValue() {
		return terminalValue;
	}

	public void setTerminalValue(String terminalValue) {
		this.terminalValue = terminalValue;
	}

	public String getScriptDir() {
		return scriptDir;
	}

	public void setScriptDir(String scriptDir) {
		this.scriptDir = scriptDir;
	}

	public void shoot(Vector3f location) {
		Long masterId = System.currentTimeMillis();

		MasterBlock masterBlock = new MasterBlock("MASTER", masterId);
		ScriptBlocksApplication.getInstance().addMasterBlock(masterId,
				masterBlock);
		masterBlock.init();
		masterBlock.setPowered(false);

		ScriptBlocksApplication.getInstance().getRootNode().getChildren()
				.add(masterBlock);
		Vector3f newLocation = location.add(ScriptBlocksApplication
				.getInstance().getCam().getDirection().mult(10.0f));
		if(levelProperties.isLockMasterToGrid()){
			int x = Math.round(newLocation.x);
			int y = Math.round(newLocation.y);
			int z = Math.round(newLocation.z);
			//The Blocks are 2 units wide
			if(x%2!=0){
				x=x+1;
			}
			if(y%2!=0){
				y=y+1;
			}
			if(z%2!=0){
				z=z+1;
			}
			masterBlock.setLocalTranslation(new Vector3f(x, y, z));
		}else {
			masterBlock.setLocalTranslation(newLocation);
		}
	}

	public boolean isNiftyActive() {
		return niftyActive;
	}

	public void setNiftyActive(boolean niftyActive) {
		this.niftyActive = niftyActive;
	}

	public ScriptEvaluator getScriptEvaluator() {
		return scriptEvaluator;
	}

	public void setScriptEvaluator(ScriptEvaluator scriptEvaluator) {
		this.scriptEvaluator = scriptEvaluator;
	}

	public String getScriptName() {
		return scriptName;
	}

	public void setScriptName(String scriptName) {
		this.scriptName = scriptName;
	}

	public GameState getGameState() {
		return gameState;
	}

	public void setGameState(GameState gameState) {
		this.gameState = gameState;
	}

	public List<BlockEvent> getBlockEventList() {
		return blockEventList;
	}

	public void setBlockEventList(List<BlockEvent> blockEventList) {
		this.blockEventList = blockEventList;
	}

	public void addBlockEvent(BlockEvent blockEvent) {
		getBlockEventList().add(blockEvent);
	}

	/**
	 * Holds the Focus Block and all its children when copied
	 */
	public String getCopyBuffer() {
		return copyBuffer;
	}

	public void setCopyBuffer(String copyBuffer) {
		this.copyBuffer = copyBuffer;
	}

	public String getCopyBlockVariable() {
		return copyBlockVariable;
	}

	public void setCopyBlockVariable(String copyBlockVariable) {
		this.copyBlockVariable = copyBlockVariable;
	}

	public String getUndoBuffer() {
		return undoBuffer;
	}

	public String getUndoBlockVariable() {
		return undoBlockVariable;
	}

	public void setUndoBuffer(String undoBuffer) {
		this.undoBuffer = undoBuffer;
	}

	public void setUndoBlockVariable(String undoBlockVariable) {
		this.undoBlockVariable = undoBlockVariable;
	}

	// Holds the Parent of the block that is being cut
	public Block getUndoBlock() {
		return undoBlock;
	}

	public void setUndoBlock(Block undoBlock) {
		this.undoBlock = undoBlock;
	}

	public Map<Integer, Integer> getWirelessFrequencyMap() {
		return wirelessFrequencyMap;
	}

	public void setWirelessFrequencyMap(
			Map<Integer, Integer> wirelessFrequencyMap) {
		this.wirelessFrequencyMap = wirelessFrequencyMap;
	}

	public Integer getWirelessFrequency(Integer key) {
		return this.getWirelessFrequencyMap().get(key);
	}

	public Integer setWirelessFrequency(Integer key, Integer value) {
		return this.getWirelessFrequencyMap().put(key, value);
	}

	public void copyHiearcy() {
		Block focusBlock = ScriptBlocksApplication.getInstance()
				.getFocusBlock();
		if (focusBlock != null && !focusBlock.isPowered()) {
			StringBuffer sb = new StringBuffer();
			String focusBlockVariable = focusBlock.getExportVariable();
			// TODO update this to use the focus side
			sb.append(focusBlock.scriptExport());
			sb.append(ScriptUtils.writeChildren(focusBlock,
					focusBlock.getExportVariable()));
			setCopyBuffer(sb.toString());
			setCopyBlockVariable(focusBlock.getExportVariable());
		}

	}

	public void copyHiearchyBeforeRemove() {
		Block focusBlock = ScriptBlocksApplication.getInstance()
				.getFocusBlock();
		if (!(focusBlock instanceof MasterBlock) && focusBlock != null
				&& !focusBlock.isPowered()) {
			StringBuffer sb = new StringBuffer();
			String focusBlockVariable = focusBlock.getExportVariable();

			sb.append(focusBlock.scriptExport());
			sb.append(ScriptUtils.writeChildren(focusBlock,
					focusBlock.getExportVariable()));
			setUndoBuffer(sb.toString());
			setUndoBlockVariable(focusBlock.getExportVariable());
			setUndoBlock(((Block) focusBlock.getParent()));
		}

	}

	public void pasteHiearcy() {
		Block focusBlock = ScriptBlocksApplication.getInstance()
				.getFocusBlock();
		String copyBlockVariable = ScriptBlocksApplication.getInstance()
				.getCopyBlockVariable();
		if (copyBlockVariable != null && focusBlock != null
				&& !focusBlock.isPowered()) {

			ScriptEvaluator pasteScriptEvaluator = new ScriptEvaluator();
			try {
				pasteScriptEvaluator.evalutateBuffer(this.getCopyBuffer());
			} catch (Exception ev) {
				ev.printStackTrace();
			}

			if (pasteScriptEvaluator != null) {
				pasteScriptEvaluator.pasteScript();
			}
			focusBlock.updateMasterId(focusBlock.getMasterId());
		}
	}

	public void undoHiearchy() {
		Block undoBlock = ScriptBlocksApplication.getInstance().getUndoBlock();
		String undoBlockVariable = ScriptBlocksApplication.getInstance()
				.getUndoBlockVariable();
		if (undoBlockVariable != null && undoBlock != null
				&& !undoBlock.isPowered()) {

			ScriptEvaluator pasteScriptEvaluator = new ScriptEvaluator();
			try {
				pasteScriptEvaluator.evalutateUndoBuffer(this.getUndoBuffer());
			} catch (Exception ev) {
				ev.printStackTrace();
			}

			if (pasteScriptEvaluator != null) {
				pasteScriptEvaluator.pasteScript();
			}
			undoBlock.updateMasterId(undoBlock.getMasterId());

			// Clean up
			setUndoBuffer(null);
			setUndoBlockVariable(null);
			setUndoBlock(null);
		}
	}

	public LevelProperties getLevelProperties() {
		return levelProperties;
	}

	public void setLevelProperties(LevelProperties levelProperties) {
		this.levelProperties = levelProperties;
	}

	public enum GameState {
		GUI, GAME;
	}
	
	public void newGame() {
		for (MasterBlock masterBlock : ScriptBlocksApplication.getInstance()
				.getMasterBlockMap().values()) {
			masterBlock.destroy();
			ScriptBlocksApplication.getInstance().getRootNode().getChildren()
			.remove(masterBlock);
		}
		
		resetLevel();
		

	}
	
	public void loadLevel(String fileName, boolean loadGUI) {
		ScriptBlocksApplication.getInstance().initScriptSave(fileName);
		if(loadGUI){
			ScriptBlocksApplication.getInstance().initNiftyGui();
		}
	}
}
